package fun.boomcat.luckyhe.mirai.plugin.bilibilivideoinfoautogenerator.utils;

import com.alibaba.fastjson.JSONObject;
import fun.boomcat.luckyhe.mirai.plugin.bilibilivideoinfoautogenerator.config.ConfigOperation;
import fun.boomcat.luckyhe.mirai.plugin.bilibilivideoinfoautogenerator.utils.entity.BilibiliSubjectObject;
import net.mamoe.mirai.message.data.*;
import net.mamoe.mirai.utils.ExternalResource;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;

public class BilibiliUtil {
    private static final String VIDEO_API = "https://api.bilibili.com/x/web-interface/view";
    private static final String VIDEO_URL = "https://www.bilibili.com/video";
    private static final String USER_VIDEO_API = "https://api.bilibili.com/x/space/arc/search";
    private static final String USER_INFO_API = "https://api.bilibili.com/x/space/acc/info";
    private static final String USER_STAT_API = "https://api.bilibili.com/x/relation/stat";
    private static final LinkedBlockingQueue<BilibiliSubjectObject> queue = new LinkedBlockingQueue<>();

    private static final Map<Long, Long> avMap = new HashMap<>();
    private static final Map<Long, String> bvMap = new HashMap<>();
    private static final Map<Long, Long> uidMap = new HashMap<>();
    private static final Thread requestThread = new Thread(() -> {
        while (true) {
            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            try {
                BilibiliSubjectObject take = queue.take();
                String requestUrl;

                if (take.getBv() != null) {
                    if (bvMap.getOrDefault(take.getContact().getId(), "default").equals(take.getBv())) {
                        continue;
                    }
                    bvMap.put(take.getContact().getId(), take.getBv());
                    requestUrl = getBVAPIUrl(take.getBv());
                    sendAVOrBVInfo(take, requestUrl);
                }
                if (take.getAv() != null) {
                    if (avMap.getOrDefault(take.getContact().getId(), 0L).equals(take.getAv())) {
                        continue;
                    }
                    avMap.put(take.getContact().getId(), take.getAv());
                    requestUrl = getAVAPIUrl(take.getAv());
                    sendAVOrBVInfo(take, requestUrl);
                }
                if (take.getUid() != null) {
                    if (uidMap.getOrDefault(take.getContact().getId(), 0L).equals(take.getUid())) {
                        continue;
                    }
                    uidMap.put(take.getContact().getId(), take.getUid());
                    sendUserAndVideoInfo(take);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });

    private static void sendUserAndVideoInfo(BilibiliSubjectObject take) throws IOException, InterruptedException {
//        获取用户信息
        Map<String, Object> userInfoObject = getJSONObjectFromUrl(getUserInfoUrl(take.getUid()));
        if (((int) userInfoObject.get("code")) != 0) {
            return;
        }
        Map<String, Object> infoDataMap = (Map<String, Object>) userInfoObject.get("data");

        ForwardMessageBuilder fmb = new ForwardMessageBuilder(take.getContact());
        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("https://space.bilibili.com/" + take.getUid()));
        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("UP\n" +
                "==========\n" +
                "" + infoDataMap.get("name")));
        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("个性签名\n" +
                "==========\n" +
                "" + infoDataMap.get("sign")));
        String facePath = URLUtil.getDownloadPicPath(((String) infoDataMap.get("face")));
        fmb.add(take.getBot().getId(), take.getBot().getNick(), ExternalResource.uploadAsImage(new File(facePath), take.getContact()));

        Map<String, Object> userStatObject = getJSONObjectFromUrl(getUserStatUrl(take.getUid()));
        if (((int) userStatObject.get("code")) != 0) {
            return;
        }

        Map<String, Object> userStatData = (Map<String, Object>) userStatObject.get("data");

        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("粉丝数/关注数\n" +
                "==========\n" +
                "" + userStatData.get("follower") + "\n" +
                "" + userStatData.get("following")));

        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("视频列表\n" +
                "==========\n" +
                "在下方的转发消息中"));
        take.getContact().sendMessage(fmb.build());

//        获取视频列表
        int page = 1;
//        take.getContact().sendMessage("开始获取视频列表");
        fmb = new ForwardMessageBuilder(take.getContact());
        int charCount = 0;
        while (page <= ConfigOperation.getMaxUserVideoPage()) {
            String requestUrl = getUserVideoInfUrl(take.getUid(), page);

            Map<String, Object> jsonObject = getJSONObjectFromUrl(requestUrl);
            int code = (int) jsonObject.get("code");
            if (code != 0) {
                return;
            }

            Map<String, Object> dataMap = (Map<String, Object>) jsonObject.get("data");
            Map<String, Object> listMap = (Map<String, Object>) dataMap.get("list");
            List<Object> vlist = (List<Object>) listMap.get("vlist");
            if (vlist.size() == 0) {
                break;
            }

            StringBuilder sb = new StringBuilder("视频列表 " + page + "\n" +
                    "==========\n");
            for (Object v : vlist) {

                Map<String, Object> vMap = (Map<String, Object>) v;
                String bvid = (String) vMap.get("bvid");
                String title = (String) vMap.get("title");
                String length = (String) vMap.get("length");
                Object createdObject = vMap.get("created");
                long created = createdObject instanceof Integer ? (int) createdObject : (long) createdObject;


                sb.append(bvid).append("    ")
                        .append(title).append("    ").append(length).append("    ")
                        .append(new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss").format(new Date(created * 1000L))).append("\n\n");
            }

            if (sb.length() > 4500) {
                take.getContact().sendMessage("有一条消息的长度大于4500，发送失败");
                return;
            }

            if (charCount + sb.length() >= 9000) {
                take.getContact().sendMessage(fmb.build());
                fmb = new ForwardMessageBuilder(take.getContact());
                charCount = 0;
                Thread.sleep(2000L);
            }
            fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText(sb.toString()));
            charCount += sb.length();

            page++;
            Thread.sleep(2000L);
        }

        if (Math.abs(page - ConfigOperation.getMaxUserVideoPage()) != 1) {
            if (fmb.size() != 0) {
                fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("获取完毕"));
                take.getContact().sendMessage(fmb.build());
            } else {
                take.getContact().sendMessage("获取完毕");
            }
        } else {
            if (fmb.size() != 0) {
                take.getContact().sendMessage(fmb.build());
            }
            take.getContact().sendMessage(ConfigOperation.getMaxUserVideoPage() + "页了跑不动了，差不多得了\n" +
                    "自己去看 https://space.bilibili.com/" + take.getUid());
        }

//        take.getContact().sendMessage("视频列表获取完毕");

    }

    private static void sendAVOrBVInfo(BilibiliSubjectObject take, String requestUrl) throws IOException {
        Map<String, Object> jsonObject = getJSONObjectFromUrl(requestUrl);

        int code = (int) jsonObject.get("code");
        if (code != 0) {
            return;
        }

        Map<String, Object> data = (Map<String, Object>) jsonObject.get("data");
        Object aidObject = data.get("aid");
        Object pubdateObject = data.get("pubdate");
        Map<String, Object> owner = (Map<String, Object>) data.get("owner");
        Object uidObject = owner.get("mid");
        Map<String, Object> stat = (Map<String, Object>) data.get("stat");
        Object staffsObject = data.get("staff");
        List<Map<String, Object>> pages = (List<Map<String, Object>>) data.get("pages");

        long aid = aidObject instanceof Integer ? (int) aidObject : (long) aidObject;
        String bvid = (String) data.get("bvid");
        String title = (String) data.get("title");
        String pic = (String) data.get("pic");
        String desc = (String) data.get("desc");
        String author = (String) owner.get("name");
        int duration = (int) data.get("duration");
        long uid = uidObject instanceof Integer ? (int) uidObject : (long) uidObject;
        String authorFace = (String) owner.get("face");
        long pubdateTimestamp = pubdateObject instanceof Integer ? (int) pubdateObject : (long) pubdateObject;
        int view = (int) stat.get("view"); // 观看
        int danmaku = (int) stat.get("danmaku"); // 弹幕
        int reply = (int) stat.get("reply"); // 评论
        int favorite = (int) stat.get("favorite"); // 收藏
        int coin = (int) stat.get("coin"); // 投币
        int share = (int) stat.get("share"); // 分享
        int like = (int) stat.get("like"); // 点赞


        ForwardMessageBuilder fmb = new ForwardMessageBuilder(take.getContact());

//                直达连接
        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText(getBVUrl(bvid)));

//                视频号
        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("视频号\n" +
                "==========\n" +
                "av号：" + aid + "\n" +
                "bv号：" + bvid));

//                获取封面
        ExternalResource picExternalResource = ExternalResource.create(new File(URLUtil.getDownloadPicPath(pic)));
        fmb.add(
                take.getBot().getId(),
                take.getBot().getNick(),
                new MessageChainBuilder().append("封面\n" +
                        "==========\n").append(
                        take.getContact().uploadImage(picExternalResource)
                ).build()
        );
        picExternalResource.close();

//                标题
        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("标题\n" +
                "==========\n" +
                "" + title));

//                作者
        if (staffsObject == null) {
            picExternalResource = ExternalResource.create(new File(URLUtil.getDownloadPicPath(authorFace)));
            fmb.add(take.getBot().getId(), take.getBot().getNick(), new MessageChainBuilder().append(new PlainText("up主\n" +
                            "==========\n" +
                            author + "\n" +
                            "UID " + uid + "\n"
                    )).append(take.getContact().uploadImage(picExternalResource))
                    .append(new PlainText("\nhttps://space.bilibili.com/" + uid)).build());
            picExternalResource.close();
        } else {
            ForwardMessageBuilder subfmb = new ForwardMessageBuilder(take.getContact());
            List<Map<String, Object>> staffs = (List<Map<String, Object>>) staffsObject;
            for (Map<String, Object> staff : staffs) {
                Object mid = staff.get("mid");
                long uidTemp = mid instanceof Integer ? (int) mid : (long) mid;
                String name = (String) staff.get("name");
                String face = (String) staff.get("face");
                String titleTemp = (String) staff.get("title");

                picExternalResource = ExternalResource.create(new File(URLUtil.getDownloadPicPath(face)));
                subfmb.add(take.getBot().getId(), take.getBot().getNick(), new MessageChainBuilder().append(new PlainText(titleTemp + "\n" +
                                "==========\n" +
                                "" + name + "\n" +
                                "UID " + uidTemp + "\n"))
                        .append(take.getContact().uploadImage(picExternalResource))
                        .append(new PlainText("\nhttps://space.bilibili.com/" + uidTemp)).build());
                picExternalResource.close();
            }

            fmb.add(take.getBot().getId(), take.getBot().getNick(), subfmb.build());
        }

//                简介
        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("简介\n" +
                "==========\n" +
                "" + desc));

        if (pages.size() > 1) {
            ForwardMessageBuilder subfmb = new ForwardMessageBuilder(take.getContact());
            List<StringBuilder> pmList = new ArrayList<>();
            int MAX = 20;
            for (int i = 0; i < pages.size(); i++) {
                String part = (String) pages.get(i).get("part");
                int time = (int) pages.get(i).get("duration");

                int li = i / MAX;
                StringBuilder stringBuilder;
                try {
                    stringBuilder = pmList.get(li);
                } catch (IndexOutOfBoundsException e) {
                    stringBuilder = new StringBuilder("视频选集 " + (li + 1) + "/" + (pages.size() % MAX == 0 ? pages.size() / MAX : pages.size() / MAX + 1) + "\n" +
                            "==========\n");
                    pmList.add(stringBuilder);
                }

                stringBuilder.append(i + 1).append("    ").append(part).append("    ")
                        .append(String.format("%02d", time / 60)).append(":").append(String.format("%02d", time % 60)).append("\n");
            }

            for (StringBuilder sb : pmList) {
                subfmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText(sb.toString()));
            }

            fmb.add(take.getBot().getId(), take.getBot().getNick(), subfmb.build());
        }

//                发布时间 时长
        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("发布时间/时长\n" +
                "==========\n" +
                "" + new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss").format(new Date(pubdateTimestamp * 1000L)) + "\n" +
                "" + String.format("%02d:%02d", duration / 60, duration % 60)));

//                统计信息
        fmb.add(take.getBot().getId(), take.getBot().getNick(), new PlainText("统计\n" +
                "==========\n" +
                "播放：" + view + "\n" +
                "弹幕：" + danmaku + "\n" +
                "点赞：" + like + "\n" +
                "投币：" + coin + "\n" +
                "收藏：" + favorite + "\n" +
                "转发：" + share + "\n" +
                "评论：" + reply));

        take.getContact().sendMessage(fmb.build());
    }

    public static void init() {
        requestThread.start();
    }

    public static String getBVAPIUrl(String bv) {
        return VIDEO_API + "?bvid=" + bv;
    }

    public static String getAVAPIUrl(long av) {
        return VIDEO_API + "?aid=" + av;
    }

    public static String getBVUrl(String bv) {
        return VIDEO_URL + "/" + bv;
    }

    public static String getUserVideoInfUrl(long uid, int page) {
        return USER_VIDEO_API + "?mid=" + uid + "&pn=" + page;
    }

    public static String getUserInfoUrl(long uid) {
        return USER_INFO_API + "?mid=" + uid;
    }

    public static String getUserStatUrl(long uid) {
        return USER_STAT_API + "?vmid=" + uid;
    }

    public static void addQueue(BilibiliSubjectObject o) {
        queue.add(o);
    }

    public synchronized static Map<String, Object> getJSONObjectFromUrl(String url) throws IOException {
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        HttpClientContext context = new HttpClientContext();
        HttpGet httpGet = new HttpGet(url);

        CloseableHttpResponse httpResponse = httpClient.execute(httpGet, context);
        Object parse = JSONObject.parse(EntityUtils.toString(httpResponse.getEntity()));

        httpResponse.close();
        httpClient.close();

        return (Map<String, Object>) parse;
    }
}
